"""
Contains convenience utility methods to be used by tests that need to connect to storage.
"""
from StringIO import StringIO
from pprint import pformat

import confUtils
from enum import Enum
from confUtils import Validate, AdvancedConfigParser, ConfigurateionValidationError

CONNECTIONS_OPTION_DEFAULT = """
[nfs1]
id=1
type=nfs
connection=server:/export
[scsi1]
type=iscsi
id=2
connection=server
port=3260
iqn=iqn.eye.queue.ein
portal=1
user=
password="""

eStorageType = Enum(
    nfs = 1,
    fcp = 2,
    iscsi = 3,
    )

__baseTemplates = {
    eStorageType.nfs : {
        "id" : {"validator" : Validate.int},
        "connection": {}
        },
    eStorageType.iscsi: {
        "id" : {"validator" : Validate.int},
        "connection": {},
        "port" : {"default" : 3260, "validator" : Validate.int},
        "iqn" : {},
        "portal" : {"default" : 1, "validator" : Validate.int},
        "user" : {"default" : ""},
        "password" : {"default" : ""}
    }
}

def _parseConnectionsSegment(connectionsSegment):
    """
    Parse the raw value to configuration object.

    :param connectionsSegment: The raw value of the connection segment.

    :returns: a :class:`confUtils.AdvancedConfigParser` representing the segment.
    """
    miniConfStr = StringIO(connectionsSegment)
    miniConfStr.seek(0)
    miniConf = AdvancedConfigParser()
    miniConf.readfp(miniConfStr)
    return miniConf

def _generateConnectionsSegmentTemplate(miniConf):
    """
    Dynamically generate a template for the Connections configuration segment

    :param miniConf: a configuration object representing a Connections segment.

    :returns: a configuration template for that Connection segment.
    """
    template = {}
    for section in miniConf.sections():
        if not miniConf.has_option(section, "type"):
            raise ConfigurateionValidationError("Every connection must declare 'type'")
        type = eStorageType.parse(miniConf.get(section, "type"))
        template[section] = __baseTemplates[type]

    return template

def validateConnections(connectionsSegment):
    """
    A validator that validates a Connections segment.

    :param connectionsSegment: The raw content of the Connections segment.

    :returns: a connections tuple in the form of ``( (connection name, storage type, connection dict), ...)``.
    """
    miniConf = _parseConnectionsSegment(connectionsSegment)
    template = _generateConnectionsSegmentTemplate(miniConf)

    confUtils.validateConfigFile(template, miniConf)
    connectionsDict = confUtils.conf2dict(template, miniConf)

    connections = []
    for name in connectionsDict:
        type = eStorageType.parse(miniConf.get(name, "type"))
        connections.append(tuple([name, type, connectionsDict[name]]))

    return tuple(connections)

def disconnectFromStorage(client, connections):
    """
    Disconnect a vdsClient from a group of storage devices.

    :param client: the vdsClient you want to work with.
    :param connections: a collection of lists ``( (connection name, storage type, connection dict), ...)``.
    """
    for name, storageType, connection in connections:
        results = {}
        results[connection['id']] = False

        resp = client.disconnectStorageServer(storageType, 0, [connection])
        if resp['status']['code'] == 0:
            for i in resp['statuslist']:
                results[i['id']] = (i['status'] == 0)

        if (False in results.values()):
            raise Exception("Could not disconnect from '%s' (%s)" % (name, pformat(resp)))

def connectToStorage(client, connections):
    """
    Connect a vdsClient to a group of storage devices.

    :param client: the vdsClient you want to work with.
    :param connections: a collection of lists ``( (connection name, storage type, connection dict), ...)``.
    """
    def connectBatch(conBatch):
        resp = client.connectStorageServer(curStorageType, 0, conBatch)
        if resp['status']['code'] == 0:
            for i in resp['statuslist']:
                results[i['id']] = (i['status'] == 0)

        if (False in results.values()):
            raise Exception("Could not connect to '%s' (%s)" % (name, pformat(resp)))

    conList = list(connections)
    conList.sort(lambda a, b: cmp(a[1], b[1]))
    curStorageType = conList[0][1]
    conBatch = []
    results = {}
    for name, storageType, connection in conList:
        if storageType != curStorageType:
            connectBatch(conBatch)
            curStorageType = storageType
            conBatch = []

        conBatch.append(connection)

    connectBatch(conBatch)
    return results

def limitTestToConnectionType(limitArray, connections):
    """
    Throw a proper error when a test is only valid to a certain type of connection.

    :param limit: Storage connection type to allow.
    :param connections: a collection of lists ``( (connection name, storage type, connection dict), ...)``.
    """
    allow = False
    for limit in limitArray:
        limit = eStorageType.parse(limit)
        for name, storageType, connection in connections:
            if limit == eStorageType.parse(storageType):
                allow = True
    if not allow:
        raise Exception("This test is only applicable to %s connections" % limitArray)
